#include "StdAfx.h"
#include "ExclusionDrawer.h"

using namespace nsExclusionStatic2;
using namespace nsCoordinateUtils;
using namespace std;

CExclusionDrawer::CExclusionDrawer( const CFont *pFont, 
	const COLORREF colArea, 
	const COLORREF colAreaShade, 
	const CRect &DrawingArea,
	const CPoint &ImageSize,
	const bool bDrawOffsetAreas ) :
	pFont_( pFont ),
	colArea_( colArea ),
	colAreaShade_( colAreaShade ),
	colUv_( RGB( 77, 150, 249 ) ),
	DrawingArea_( DrawingArea ),
	DrawingAreaClipped_( 0, 0, DrawingArea.Width() - 1, DrawingArea.Height() - 1 ),
	bDrawOffsetAreas_( bDrawOffsetAreas )
{
	nDivX_ = ( float )DrawingArea.Width() / ImageSize.x;
	nDivY_ = ( float )DrawingArea.Height() / ImageSize.y;
}

CExclusionDrawer::~CExclusionDrawer(void)
{
}

// Draw all exclusions in vExclusionAreas-array
//
// - draw selected exclusion with anchors and offset (if present)
// - skip disabled areas
//
// The drawing must be done in two stages, i.e. this function must be called twice with
// different DrawModes
//
// 1. All unselected areas (incl. offset) -> edmNonselected
// 2. The selected ares (incl. offset) -> edmSelected
//
// Each drawing stage is done in several steps to avoid overdraw effects:
// 1. Fill all areas solid
// 2. Draw all rectangles around areas (i.e. all frames are always visible)
// 3. Write counter inside areas (i.e. all counters overdraw all other graphic)
//
void CExclusionDrawer::drawExclusions( CMemDC *pDC, 
	const EExclusionDrawMode DrawMode, 
	const vector<SEXCLUSION> &vExclusionAreas, 
	const int iSelected )
{
	CFont *pFontOld = 0;
	CBrush *pBrushOld = 0;
	CPen *pPenOld = 0;

	CRgn ClippingRegion;
	ClippingRegion.CreateRectRgn( DrawingArea_.left, DrawingArea_.top, DrawingArea_.right, DrawingArea_.bottom );
	pDC->SelectClipRgn( &ClippingRegion );

	{
		size_t i,
			nSize = vExclusionAreas.size();
		CRect Rect,
			rectOffset;

		// fill areas
		for ( i = 0; i < nSize; i++)
		{
			const SArea *pArea = &vExclusionAreas[ i ].Area;
			correctRect( &Rect, &pArea->Rect );
			rectOffset = Rect;
			rectOffset.OffsetRect( pArea->nOffsetX, pArea->nOffsetY );
			logicalToScreen( &Rect );
			logicalToScreen( &rectOffset );


			// nonselected area
			if ( DrawMode == edmNonselected && i != iSelected )
			{
//				drawSolidRect( pDC, &Rect, colAreaShade_ );
				drawSolidRect( pDC, &Rect, colArea_ );
			}

			// selected area
			if ( DrawMode == edmSelected && i == iSelected )
			{
				// offset area
				if ( bDrawOffsetAreas_ ) drawSolidRect( pDC, &rectOffset, colUv_ );

				// area (overdraws offset area)
				drawHatchRect( pDC, &Rect, 0x734932, 0xFFFFFF );
			}
		}

		CBrush br, brUv;
		br.CreateSolidBrush( (COLORREF) colAreaShade_ );
		brUv.CreateSolidBrush( (COLORREF) colUv_ );
		// draw frames around areas
		for ( i = 0; i < nSize; i++)
		{
			const SArea *pArea = &vExclusionAreas[ i ].Area;
			correctRect( &Rect, &pArea->Rect );
			rectOffset = Rect;
			rectOffset.OffsetRect( pArea->nOffsetX, pArea->nOffsetY );
			logicalToScreen( &Rect );
			logicalToScreen( &rectOffset );

			// area (draw only for current stage)
			if ( ( DrawMode == edmSelected && i == iSelected ) ||
				 ( DrawMode == edmNonselected && i != iSelected ) )
			{
				drawFrame( pDC, &Rect, &br );
			}


			// offset area (only nonselected, which is already drawn previously)
			if ( DrawMode == edmNonselected && i != iSelected )
			{
				if ( bDrawOffsetAreas_ && i != iSelected ) drawFrame( pDC, &rectOffset, &brUv );
			}

			// draw anchors (only around selected frame)
			if ( DrawMode == edmSelected && i == iSelected )
			{
				drawAnchors( pDC, &Rect );
			}
		}



		// select font for DrawCounter
		pFontOld = pDC->SelectObject( (CFont *)pFont_ );

		br.DeleteObject();
		br.CreateSolidBrush( (COLORREF)0xFFFFFF );
		pBrushOld = pDC->SelectObject( &br );
		CPen pen;
		pen.CreatePen( PS_SOLID, 0, colArea_ );
		pPenOld = pDC->SelectObject( &pen );
		pDC->SetBkMode(TRANSPARENT);

		// draw numbers and frames (overdraw) but current area
		for ( i = 0; i < nSize; i++)
		{
			correctRect( &Rect, &vExclusionAreas[ i ].Area.Rect );
			logicalToScreen( &Rect );

			// area (draw only for current stage)
			if ( ( DrawMode == edmSelected && i == iSelected ) ||
				 ( DrawMode == edmNonselected && i != iSelected ) )
			{
				drawCounter( i, pDC, &Rect );
			}
		}

	}

	// restore objects
	if ( pFontOld ) pDC->SelectObject( pFontOld );
	if ( pBrushOld ) pDC->SelectObject( pBrushOld );
	if ( pPenOld ) pDC->SelectObject( pPenOld );

	// and clipping region
	pDC->SelectClipRgn( 0 );
}

void CExclusionDrawer::logicalToScreen( CRect *pRect )
{
/*
	pRect->left *= nDivX;
	pRect->right *= nDivX;

	pRect->top *= nDivY;
	pRect->bottom *= nDivY;
*/
	// rounding correction (user middle of logical point)
	// '( nDivX < 1.0 ? 1 : 0 )' is to align the area's border perfectly to the drawing area if
	// aspect ratio is < 1 (i.e. if one screen pixel represents multiple printer's pixels)
	pRect->left = (LONG)( ( ( float )pRect->left ) * nDivX_ );
	pRect->right = (LONG)( ( ( float )pRect->right + 1 ) * nDivX_ + ( nDivX_ < 1.0 ? 1 : 0 ) );

	pRect->top = (LONG)( ( ( float )pRect->top ) * nDivY_ );
	pRect->bottom = (LONG)( ( ( float )pRect->bottom + 1 ) * nDivY_ + ( nDivY_ < 1.0 ? 1 : 0 ) );

	pRect->OffsetRect( DrawingArea_.left, DrawingArea_.top );
}

void CExclusionDrawer::drawSolidRect( CMemDC *pDC, CRect *pRect, COLORREF col )
{
	pDC->FillSolidRect( pRect, col );
}

void CExclusionDrawer::drawHatchRect( CMemDC *pDC, CRect *pRect, COLORREF colForeground, COLORREF colBackground )
{
	CBrush br;
	br.CreateHatchBrush( HS_BDIAGONAL, colForeground );
	pDC->SetBkColor( colBackground );
	pDC->SetBkMode( TRANSPARENT );

	CRect rInnerFrame = *pRect;

	rInnerFrame.DeflateRect( 1, 1, 1, 1 );

	pDC->FillRect( &rInnerFrame, &br );
}

void CExclusionDrawer::drawCounter( int iCount, CMemDC *pDC, CRect *pRect )
{
	CString str;
	str.Format( _T("%d"), iCount+1 );

	CPoint pnt;
	pnt = pDC->GetTextExtent( str );

	pDC->BeginPath();
	pDC->TextOut( pRect->left + pRect->Width() / 2 - pnt.x / 2, pRect->top + pRect->Height() / 2 - pnt.y / 2, str, str.GetLength() );
	pDC->EndPath();
//	pDC->FillPath();
	pDC->StrokeAndFillPath();
//	pDC->StrokePath();
}

void CExclusionDrawer::drawFrame( CMemDC *pDC, CRect *pRect, CBrush *pBrush )
{
	pDC->FrameRect( pRect, pBrush );
}

void CExclusionDrawer::drawExclusionFrameSelected( CMemDC *pDC, CRect *pRect )
{
	CBrush br( colArea_ );
	pDC->FrameRect( pRect, &br );

	CRect rInnerFrame = *pRect;

	rInnerFrame.DeflateRect( 1, 1, 1, 1 );
	br.DeleteObject();
	br.CreateSolidBrush( 0xFFFFFF );
	pDC->FrameRect( &rInnerFrame, &br );


//	br.DeleteObject();
	rInnerFrame.DeflateRect( 1, 1, 1, 1 );
//	br.CreateHatchBrush(
	br.DeleteObject();
	br.CreateHatchBrush( HS_BDIAGONAL, (COLORREF)0x734932 );

	pDC->SetBkColor( (COLORREF) 0xFFFFFF );
	pDC->SetBkMode(TRANSPARENT);

	pDC->FillRect( &rInnerFrame, &br );
}

void CExclusionDrawer::drawAnchors( CMemDC *pDC, CRect *pRect )
{
	// draw anchors
	int iXMid = pRect->left + pRect->Width() / 2,
		iYMid = pRect->top + pRect->Height() / 2;

	// top
	drawAnchor( eaTopLeft, pDC, pRect->left,		pRect->top );	// left
	drawAnchor( eaTopMiddle, pDC, iXMid,			pRect->top );		// middle
	drawAnchor( eaTopRight, pDC, pRect->right, pRect->top );		// bottom

	// mid
	drawAnchor( eaMiddleLeft, pDC, pRect->left,		iYMid );			// left
	drawAnchor( eaMiddleRight, pDC, pRect->right,		iYMid );			// right

	// bottom
	drawAnchor( eaBottomLeft, pDC, pRect->left,		pRect->bottom );	// left
	drawAnchor( eaBottomMiddle, pDC, iXMid,			pRect->bottom );		// middle
	drawAnchor( eaBottomRight, pDC, pRect->right, pRect->bottom );			// right

	// store SCREEN coordinates of anchor
	vAnchors_[ eaInside ] = *pRect;

}

void CExclusionDrawer::drawAnchor( EANCHOR Anchor, CMemDC *pDC, int x, int y )
{
	CRect Rect( x, y, x, y );
	Rect.InflateRect( 4, 4, 4, 4 );
	vAnchors_[ Anchor ] = Rect;

	CBrush br( (COLORREF)0xFFFFFF );
	pDC->FrameRect( &Rect, &br );

	Rect.DeflateRect( 1, 1, 1, 1 );
	drawSolidRect( pDC, &Rect, colArea_ );
}

void CExclusionDrawer::getAnchors( OUT vector<CRect> &vAnchors )
{
	ASSERTR( vAnchors.size() == eaMax );

	size_t i;
	for ( i = 0; i < eaMax; ++i )
	{
		vAnchors[ i ] = vAnchors_[ i ];
	}
}